#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "x86.h"
#include "traps.h"
#include "spinlock.h"

// Interrupt descriptor table (shared by all CPUs).
struct gatedesc idt[256];
extern uint vectors[];  // in vectors.S: array of 256 entry pointers
struct spinlock tickslock;
uint ticks;

//实验函数
#define STACK_PUSH(offset, val) *(uint *)(tf->esp - (offset)) = (val)
static void do_alarm(struct trapframe *tf);
static void do_malloc(struct trapframe *tf);
extern int mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm);

void
tvinit(void)
{
  int i;

  for(i = 0; i < 256; i++)
    SETGATE(idt[i], 0, SEG_KCODE<<3, vectors[i], 0);
  SETGATE(idt[T_SYSCALL], 1, SEG_KCODE<<3, vectors[T_SYSCALL], DPL_USER);

  initlock(&tickslock, "time");
}

void
idtinit(void)
{
  lidt(idt, sizeof(idt));
}

//PAGEBREAK: 41
void
trap(struct trapframe *tf)
{
  if(tf->trapno == T_SYSCALL){
    if(myproc()->killed)
      exit();
    myproc()->tf = tf;
    syscall();
    if(myproc()->killed)
      exit();
    return;
  }

  switch(tf->trapno){
  case T_IRQ0 + IRQ_TIMER:
    if(cpuid() == 0){
      acquire(&tickslock);
      ticks++;
      wakeup(&ticks);
      release(&tickslock);
    }
    //时钟中断检测
    do_alarm(tf);

    lapiceoi();
    break;
  case T_IRQ0 + IRQ_IDE:
    ideintr();
    lapiceoi();
    break;
  case T_IRQ0 + IRQ_IDE+1:
    // Bochs generates spurious IDE1 interrupts.
    break;
  case T_IRQ0 + IRQ_KBD:
    kbdintr();
    lapiceoi();
    break;
  case T_IRQ0 + IRQ_COM1:
    uartintr();
    lapiceoi();
    break;
  case T_IRQ0 + 7:
  case T_IRQ0 + IRQ_SPURIOUS:
    cprintf("cpu%d: spurious interrupt at %x:%x\n",
            cpuid(), tf->cs, tf->eip);
    lapiceoi();
    break;
  case T_PGFLT:
    do_malloc(tf);
    break;
      
  //PAGEBREAK: 13
  default:
    if(myproc() == 0 || (tf->cs&3) == 0){
      // In kernel, it must be our mistake.
      cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n",
              tf->trapno, cpuid(), tf->eip, rcr2());
      panic("trap");
    }
    // In user space, assume process misbehaved.
    cprintf("pid %d %s: trap %d err %d on cpu %d "
            "eip 0x%x addr 0x%x--kill proc\n",
            myproc()->pid, myproc()->name, tf->trapno,
            tf->err, cpuid(), tf->eip, rcr2());
    myproc()->killed = 1;
  }

  // Force process exit if it has been killed and is in user space.
  // (If it is still executing in the kernel, let it keep running
  // until it gets to the regular system call return.)
  if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER)
    exit();

  // Force process to give up CPU on clock tick.
  // If interrupts were on while locks held, would need to check nlock.
  if(myproc() && myproc()->state == RUNNING &&
     tf->trapno == T_IRQ0+IRQ_TIMER)
    yield();

  // Check if the process has been killed since we yielded
  if(myproc() && myproc()->killed && (tf->cs&3) == DPL_USER)
    exit();
}


static void do_malloc(struct trapframe *tf){
  uint va = rcr2(); //获取触发异常的虚拟地址
  struct proc *curproc = myproc();

  //检查访问地址是否合法
  if(va >= curproc->sz){
    cprintf("pid %d %s: invalid address page fault at %x\n", curproc->pid, curproc->name, va);
    curproc->killed = 1;
    return;
  }

  //分配一页物理内存
  char *mem = kalloc();
  if(!mem){
    cprintf("out of memory\n");
    curproc->killed = 1;
    return;
  }
  memset(mem, 0, PGSIZE);

  //映射到页表中
  void *page_start = (void *)PGROUNDDOWN(va);
  if(mappages(curproc->pgdir, page_start, PGSIZE, V2P(mem), PTE_W|PTE_U) < 0){
    cprintf("mappages failed\n");
    kfree(mem);
    curproc->killed = 1;
    return;
  }

  return;
}


static void do_alarm(struct trapframe *tf){
  //检查是否有正在运行的进程且中断来自用户空间且正在执行alarmhandler
  if(myproc() != 0 && (tf->cs & 3) == 3 && myproc()->alarmhandler == 0 && myproc()->alarmflag == 0){
    //cprintf("pid %d: alarmticks=%d\n", myproc()->pid, myproc()->alarmticks);  // 调试信息
    if(myproc()->alarmticks > 0){
      myproc()->alarmticks--;
      //cprintf("pid %d: alarmticks=%d\n", myproc()->pid, myproc()->alarmticks);  // 调试信息
      if(myproc()->alarmticks == 0){
        
        myproc()->alarmticks = myproc()->alarmticks_initial;
        myproc()->alarmflag = 1;

        /* 保存寄存器 */

        // 指令指针
        STACK_PUSH(0x04, tf->eip);

        // 调用者保存寄存器
        // eax, edx, ecx
        STACK_PUSH(0x08, tf->eax);
        STACK_PUSH(0x0c, tf->edx);
        STACK_PUSH(0x10, tf->ecx);

        // 被调用者保存寄存器
        // ebx, ebp, esi, edi
        STACK_PUSH(0x14, tf->ebx);
        STACK_PUSH(0x18, tf->ebp);
        STACK_PUSH(0x1c, tf->esi);
        STACK_PUSH(0x20, tf->edi);

        
        /* 插入系统调用 */

        /*
         * movl SYS_alarm_save, %eax
         * add hardcode_len, %esp
         * int $0x40
         * ret
         */

        STACK_PUSH(0x24, 0x00c340cd);
        STACK_PUSH(0x28, 0x0cc48300);
        STACK_PUSH(0x2c, 0x000019b8);

        /* 返回地址指向栈上指令 */
        STACK_PUSH(0x30, tf->esp - 0x2c);

        /* 更新用户栈 */
        tf->esp -= 0x30;

        //设置eip为alarmhandler的地址
        tf->eip = (uint)myproc()->alarmhandler;
        
        //cprintf("pid %d: alarmticks=%d\n", myproc()->pid, myproc()->alarmticks);//执行完alarmhandler后，不返回内核空间，而是继续执行用户空间的代码
      }
    }
  }
}